package org.lex.dbq.model.jdbc.template.parser;

public class FragmentLexer {
	private static final String SEP = "<${}>";

	private CharSequence src = null;
	private int offset = 0;

	public FragmentLexer(CharSequence src) {
		this.src = src;
	}

	public Fragment getNext() {
		int startIndex = offset;
		int ch = 0;
		String image = null;
		Fragment f = null;
		while (null == f && -1 != (ch = read())) {
			switch (ch) {
			case '<':// OptionalFragment start
				f = parseOptionalFragment(startIndex);
				break;
			case '>':// OptionalFragment end:ERROR
				break;
			case '$':// VariableFragment prefix
				while (true) {
					ch = read();
					if (-1 == ch || '}' == ch) {
						break;
					}
				}
				image = src.subSequence(startIndex, offset).toString();
				f = new VariableFragment(image);
				break;
			case '{':// VariableFragment start:ERROR
				break;
			case '}':// VariableFragment end:ERROR
				break;
			default:// StringFragment
				while (true) {
					ch = read();
					if (-1 == ch) {
						break;
					}
					if (-1 != SEP.indexOf(ch)) {
						unread();
						break;
					}
				}
				image = src.subSequence(startIndex, offset).toString();
				f = new StringFragment(image);
				break;
			}
		}
		return f;
	}

	private OptionalFragment parseOptionalFragment(int startIndex) {
		int count = 0;
		int ch = 0;
		while (-1 != (ch = read())) {
			if ('<' == ch) {
				count++;
			}
			if ('>' == ch) {
				if (0 == count) {
					break;
				} else {
					count--;
				}
			}
		}
		String image = src.subSequence(startIndex, offset).toString();
		OptionalFragment f = new OptionalFragment(image);
		return f;
	}

	private int read() {
		return offset < src.length() ? src.charAt(offset++) : -1;
	}

	private void unread() {
		offset--;
	}

	public static void main(String[] args) {
		FragmentLexer lexer = new FragmentLexer("host<:${port}></${database}</${table}</${column}>>>");
		while (true) {
			Fragment f = lexer.getNext();
			if (null == f) {
				break;
			} else {
				System.out.println(f);
			}
		}
	}
}
